﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.IO;
using System.Text.RegularExpressions;
using System.Collections;
using System.Xml;
using ProxyList;

namespace ProxyList
{
    public delegate void AsyncOperationInvoker(AsyncOperation operation);

    public partial class frmMain : Form
    {
        string hideme_url = "http://hideme.ru";

        private int sortColumn = -1;
        private bool isChecking = false;
        private CheckerManager manager = null;

        private CookieCollection cookies = new CookieCollection();

        public frmMain()
        {
            InitializeComponent();
            statusBar.ProgressBar.Width = statusStrip.ClientSize.Width - 24;

            manager = new CheckerManager();
            manager.onCheck += new AfterProxyCheck(UpdateState);
            manager.onComplete += new WorkComplete(CompleteChecking);
        }

        private void frmMain_Resize(object sender, EventArgs e)
        {
        }

        private void menuStrip1_Resize(object sender, EventArgs e)
        {
            int w = menuStrip.ClientSize.Width - 10;
            w -= proxyToolStripMenuItem.Width;
            tboxURL.Width = w;
        }

        private void listProxies_Resize(object sender, EventArgs e)
        {
            int w = listProxies.ClientSize.Width;
            int n = listProxies.Columns.Count - 1;
            for(int i = 0; i < n; i++)
                w -= listProxies.Columns[i].Width;

            headInfo.Width = w;
        }

        private void statusStrip_Resize(object sender, EventArgs e)
        {
            statusBar.ProgressBar.Width = statusStrip.ClientSize.Width - 24;
        }

        private void loadToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Cursor.Current = Cursors.WaitCursor;
            try
            {
                LoadProxies();
            }
            finally
            {
                Cursor.Current = Cursors.Default;
            }
        }

        private void openToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
                OpenProxies(openFileDialog1.FileName);
        }

        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (saveFileDialog1.ShowDialog() == DialogResult.OK)
                SaveProxies(saveFileDialog1.FileName);
        }

        private void checkToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (isChecking)
            {
                Cursor = Cursors.WaitCursor;
                try
                {
                    checkToolStripMenuItem.Enabled = false;
                    manager.Stop();
                }
                finally
                {
                    Cursor = Cursors.Default;
                }
            }
            else
            {
                string s = tboxURL.Text;
                if (!s.ToLower().StartsWith("http://") && !s.ToLower().StartsWith("https://")) 
                    s = "http://" + tboxURL.Text;

                if (Uri.IsWellFormedUriString(s, UriKind.Absolute))
                {
                    tboxURL.Text = s;
                    checkToolStripMenuItem.Text = "Stop";
                    CheckProxies();
                }
                else
                {
                    MessageBox.Show("URL is not valid", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
            isChecking = !isChecking;
        }

        private void clearToolStripMenuItem_Click(object sender, EventArgs e)
        {
            List<ListViewItem> items = new List<ListViewItem>();
            foreach (ListViewItem item in listProxies.Items)
            {
                if (String.IsNullOrEmpty(item.SubItems[3].Text)) continue;
                items.Add(item);
            }

            foreach(ListViewItem item in items)
                listProxies.Items.Remove(item);
        }

        private void SaveProxies(string FileName)
        {
            using (StreamWriter writer = new StreamWriter(FileName))
            {
                foreach (ListViewItem item in listProxies.Items)
                {
                    // if(String.IsNullOrEmpty(item.SubItems[3].Text)) 
                        writer.WriteLine(item.Text + ":" + item.SubItems[1].Text);
                }
            }
        }

        private void OpenProxies(string FileName)
        {
            listProxies.Items.Clear();
            string line;
            using (StreamReader reader = new StreamReader(FileName))
                while((line = reader.ReadLine()) != null)
                {
                    try
                    {
                        ProxyInfo a = new ProxyInfo();
                        a.Assign(line);
                        ListViewItem item = new ListViewItem(new string[] { a.Address, a.Port, "", "" });
                        listProxies.Items.Add(item);
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e.Message);
                    }
                }
        }

        private void LoadProxies()
        {
            string html = LoadProxyPage();
            ExtractProxies(html);
            // AssignListView();
        }

        private void CheckProxies()
        {
            if (isChecking) return;



            statusBar.Maximum = listProxies.Items.Count;
            statusBar.Value = 0;

            List<ProxyInfo> list = new List<ProxyInfo>();
            foreach (ListViewItem item in listProxies.Items)
            {
                ProxyInfo addr  = new ProxyInfo();
                addr.Address = item.Text;
                addr.Port = item.SubItems[1].Text;
                item.SubItems[2].Text = String.Empty;
                item.SubItems[3].Text = String.Empty;
                item.Tag = null;
                addr.Item = item;
                list.Add(addr);
            }

            manager.URL = tboxURL.Text;

            // someData - это данные которые надо передать в рабочий поток (см. метод WorkerThread)
            // в методе WorkerThread данные доступны через operation.UserSuppliedState;
            // чтобы из рабочего потока можно было "присоединиться" к текущему потоку.
            AsyncOperation ao = AsyncOperationManager.CreateOperation(list);
            // запустить рабочий поток
            new AsyncOperationInvoker(manager.WorkerThread).BeginInvoke(ao, null, null);

            //checker.OnShowInfo += OnShowInfo;
            //checker.Execute(list);
        }

        public void UpdateState(object Obj)
        {
            ProxyInfo info = Obj as ProxyInfo;
            ListViewItem item = info.Item as ListViewItem;
            item.SubItems[2].Text = info.MSec;
            item.SubItems[3].Text = info.Info;
            item.Tag = info;
            statusBar.Increment(1);

        }

        public void CompleteChecking(object Obj)
        {
            statusBar.Value = 0;
            checkToolStripMenuItem.Enabled = true;
            isChecking = false;
            checkToolStripMenuItem.Text = "Check";
        }

        private void ExtractProxies(string Html)
        {
            PicParser parser = new PicParser();

            listProxies.Items.Clear();
            Regex re = new Regex("<tr[^>]*><td[^>]*>(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})</td><td>(.*?)</td>", RegexOptions.Singleline | RegexOptions.IgnoreCase);
            MatchCollection mc = re.Matches(Html);
            foreach (Match m in mc)
            {
                try
                {
                    string port_info = m.Groups[2].Value.Trim();
                    
                    string port = "0";
                    try
                    {
                        Int32.Parse(port_info);
                        port = port_info;
                    }
                    catch
                    {
                        try
                        {
                            int pos = port_info.IndexOf('"');
                            port_info = port_info.Substring(pos + 1);
                            port_info = port_info.Substring(0, port_info.IndexOf('"'));
                            string pic_url = hideme_url + port_info;
                            Bitmap bmp = Download(pic_url);
                            port = parser.Execute(bmp);
                        }
                        catch
                        {
                        }
                    }
                    if(!port.Equals("0"))
                    {
                        ListViewItem item = new ListViewItem(new string[] { m.Groups[1].Value.Trim(), port, "", "" });
                        listProxies.Items.Add(item);
                    }
                }
                catch
                {
                }
            }
        }

        private void ExtractImgProxies(string Html)
        {
            PicParser parser = new PicParser();

            listProxies.Items.Clear();
            Regex re = new Regex("<tr[^>]*><td[^>]*>(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})</td><td><img src=\"([^\"]+)\"", RegexOptions.Singleline | RegexOptions.IgnoreCase);
            MatchCollection mc = re.Matches(Html);
            foreach (Match m in mc)
            {
                try
                {
                    string pic_url = hideme_url + m.Groups[2].Value.Trim();
                    Bitmap bmp = Download(pic_url);
                    string port = parser.Execute(bmp);
                    ListViewItem item = new ListViewItem(new string[] { m.Groups[1].Value.Trim(), port, "", "" });
                    listProxies.Items.Add(item);
                }
                catch
                {
                }
            }
        }

        private Bitmap Download(string URL)
        {
            Bitmap bmp = null;

            WebRequest req = System.Net.WebRequest.Create(URL);

            req.ContentType = "application/x-www-form-urlencoded";
            req.Method = "GET";

            using (WebResponse resp = req.GetResponse())
            {
                if (resp == null) return null;

                using (Stream remoteStream = resp.GetResponseStream())
                {
                    bmp = new Bitmap(remoteStream);
                }
            }
            return bmp;
        }

        private string LoadProxyPage()
        {
            // HttpWebRequest req = (HttpWebRequest)WebRequest.Create("http://hideme.ru/proxy-list/http");
            HttpWebRequest req = (HttpWebRequest)WebRequest.Create(hideme_url + "/proxy-list/?type=h");
            req.Method = "GET";
            req.KeepAlive = true;
            req.ContentType = "application/x-www-form-urlencoded";
            req.UserAgent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.5 (KHTML, like Gecko) Chrome/4.0.249.89 Safari/532.5";
            req.Referer = hideme_url + "/proxy-list/";
            // req.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8";

            req.Headers.Add("Cache-Control", "max-age=0");
            // req.Headers.Add("Accept-Encoding", "gzip,deflate,sdch");

            try
            {
                string[] parts = tboxProxy.Text.Split(':');
                if (parts.Length == 2)
                {
                    req.Proxy = new WebProxy(parts[0], Int32.Parse(parts[1]));
                }
            }
            catch
            {
                req.Proxy = null;
            }

            req.CookieContainer = new CookieContainer();
            req.CookieContainer.Add(cookies);

            HttpWebResponse resp = req.GetResponse() as HttpWebResponse;
            if (resp == null) return String.Empty;

            string html = "";
            using (StreamReader sr = new StreamReader(resp.GetResponseStream(), Encoding.GetEncoding(1251)))
            //using (StreamReader sr = new StreamReader(resp.GetResponseStream(), Encoding.UTF8))
            {
                html = sr.ReadToEnd();
            }

            cookies = resp.Cookies;

            File.WriteAllText("ans.html", html);

            html = html.Replace("\r", "").Replace("\n", Environment.NewLine);
            html = html.Trim();

            return html;
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void copyToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (this.InvokeRequired)
                this.Invoke((SimpleHandle)CopyToBuffer);
            else
                CopyToBuffer();
        }

        delegate void SimpleHandle();
        private void CopyToBuffer()
        {
            string buf = String.Empty;
            foreach (ListViewItem item in listProxies.SelectedItems)
            {
                if (!String.IsNullOrEmpty(buf))
                    buf += Environment.NewLine;
                buf += item.Text + ":" + item.SubItems[1].Text;
            }
            //Clipboard.Clear();
            //Clipboard.SetText(buf);
            Clipboard.SetDataObject(buf, false, 5, 100);
        }

        private void listProxies_ColumnClick(object sender, ColumnClickEventArgs e)
        {
            // Определение того, совпадает ли столбец с последним выбранным столбцом.

            if (e.Column != sortColumn)
            {
                // Установка сортировки нового столбца.
                sortColumn = e.Column;
                // Установка порядка сортировки "по возрастанию" как порядка по
                // умолчанию.
                listProxies.Sorting = SortOrder.Ascending;
            }
            else
            {
                // Определение и последующее изменение последнего порядка сортировки.
                if (listProxies.Sorting == SortOrder.Ascending)
                    listProxies.Sorting = SortOrder.Descending;
                else
                    listProxies.Sorting = SortOrder.Ascending;
            }

            // Вызов метода ручной сортировки.
            listProxies.Sort();
            // Установка свойства ListViewItemSorter на новый объект
            // ListViewItemComparer.
            this.listProxies.ListViewItemSorter = new ListViewItemComparer(e.Column, listProxies.Sorting);
        }

        private void listProxies_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            ListViewItem item = listProxies.GetItemAt(e.X, e.Y);
            if (item != null)
                MessageBox.Show((item.Tag as ProxyInfo).Answer, item.Text);
        }
    }

    // Реализация ручной сортировки элементов по столбцам.
    class ListViewItemComparer : IComparer
    {
        private int col;
        private SortOrder order;
        
        public ListViewItemComparer()
        {
            col = 0;
            order = SortOrder.Ascending;
        }

        public ListViewItemComparer(int column, SortOrder order)
        {
            col = column;
            this.order = order;
        }

        public int Compare(object x, object y)
        {
            int returnVal = -1;

            string sx = ((ListViewItem)x).SubItems[col].Text;
            string sy = ((ListViewItem)y).SubItems[col].Text;

            if((col == 1) ||(col == 2))
            {
                int ix = String.IsNullOrEmpty(sx) ? 0 : Int32.Parse(sx);
                int iy = String.IsNullOrEmpty(sy) ? 0 : Int32.Parse(sy);
                returnVal = (ix == iy) ? 0 : ((ix > iy) ? 1 : -1);
            }
            else
                returnVal = String.Compare(sx, sy);

            if (order == SortOrder.Descending)
                // Изменение значения, возвращенного String.Compare, на
                // противоположное.
                returnVal *= -1;
            return returnVal;
        }
    }

    public class ProxyInfo
    {
        public object Item;
        public string Address;
        public string Port;
        public string MSec;
        public string Info;
        public string Answer;

        public void Assign(string Line)
        {
            string[] parts = Line.Split(new char[]{':'}, StringSplitOptions.RemoveEmptyEntries);
            if(parts.Length == 2)
            {
                Address = parts[0];
                Port = parts[1];
            }
            else
                throw new Exception("Error address format - "+ Line);
        }
    }
}
